// Fill out your copyright notice in the Description page of Project Settings.


#include "ScreenshotViewportClient.h"

#include "Engine/World.h"
#include "Engine/UserInterfaceSettings.h"
#include "GameFramework/PlayerController.h"
#include "Engine/Canvas.h"
#include "EngineModule.h"
#include "LegacyScreenPercentageDriver.h"
#include "Engine/Engine.h"
#include "SceneViewExtension.h"
#include "ContentStreaming.h"
#include "Camera/CameraTypes.h"
#include "GameFramework/playerController.h"
#include "GameFramework/WorldSettings.h"
#include "BufferVisualizationData.h"
#include "UnrealEngine.h"
#include "Engine/Engine.h"
#include "DynamicResolutionState.h"

#include "Components/LineBatchComponent.h"

#include <Runtime/Launch/Resources/Version.h>


//#include "Engine/LocalPlayer.h"

#include "STShotLocalPlayer.h"

FScreenshotViewportClient::FScreenshotViewportClient() :
	worldData(), screenshotInfo()
	,ScreenshotShowFlags(ESFIM_Game)
	, CurrentBufferVisualizationMode(NAME_None)
	, CurrentNaniteVisualizationMode(NAME_None)
	, CurrentLumenVisualizationMode(NAME_None)
	, CurrentStrataVisualizationMode(NAME_None)
	, CurrentGroomVisualizationMode(NAME_None)
	, CurrentVirtualShadowMapVisualizationMode(NAME_None)
	, calcSceneLocalPlayer(nullptr)
{
}

FScreenshotViewportClient::~FScreenshotViewportClient()
{
	if (!calcSceneLocalPlayer)
	{
		calcSceneLocalPlayer->RemoveFromRoot();
	}
	calcSceneLocalPlayer = nullptr;
	FlushRenderingRenderTask();
}

FScreenshotViewportClient::FWorldData::FWorldData()
	:world(nullptr)
	, gameInstance(nullptr)
	, gameViewportClient(nullptr)

{

}

FScreenshotViewportClient::FWorldData::FWorldData(UWorld* w)
	:world(w)
{
	if (world) 
	{
		gameInstance = world->GetGameInstance();
		gameViewportClient = world->GetGameViewport();
	}
}



void FScreenshotViewportClient::InitViewportClient(UWorld* world) 
{
	check(world);
	worldData = FWorldData(world);
}

void FScreenshotViewportClient::InitRenderTask(const FScreenshotInfo& info, FViewport* InViewport)
{
	screenshotInfo = info;

	if (!calcSceneLocalPlayer)
	{
		calcSceneLocalPlayer = NewObject<USTShotLocalPlayer>(GEngine, USTShotLocalPlayer::StaticClass());
		calcSceneLocalPlayer->AddToRoot();
	}
	calcSceneLocalPlayer->SetViewPoint(info);

}

void FScreenshotViewportClient::FlushRenderingRenderTask() {

	screenshotInfo = {};
	if (calcSceneLocalPlayer)
	{
		calcSceneLocalPlayer->ReleaseView();
	}
}

void FScreenshotViewportClient::SetEngineShowFlags(FEngineShowFlags Flags)
{
	ScreenshotShowFlags = Flags;
}


UWorld* FScreenshotViewportClient::GetWorld() const 
{
	return worldData.world;
}

//~ Begin FViewportClient Interface.

void FScreenshotViewportClient::Precache()
{
	static bool bIsFirstCallOfFunction = true;
	if (bIsFirstCallOfFunction)
	{
		UE_LOG(LogPlayerManagement, Log, TEXT("%5.2f seconds passed since startup."), FPlatformTime::Seconds() - GStartTime);
		bIsFirstCallOfFunction = false;
	}
}

/** Util to find named canvas in transient package, and create if not found */
UCanvas* FScreenshotViewportClient::GetCanvasByName(FName CanvasName)
{
	// Cache to avoid FString/FName conversions/compares
	static TMap<FName, UCanvas*> CanvasMap;
	UCanvas** FoundCanvas = CanvasMap.Find(CanvasName);
	if (!FoundCanvas)
	{
		UCanvas* CanvasObject = FindObject<UCanvas>(GetTransientPackage(), *CanvasName.ToString());
		if (!CanvasObject)
		{
			CanvasObject = NewObject<UCanvas>(GetTransientPackage(), CanvasName);
			CanvasObject->AddToRoot();
		}

		CanvasMap.Add(CanvasName, CanvasObject);
		return CanvasObject;
	}

	return *FoundCanvas;
}

FSceneView* GetViewTest(UWorld* MyWorld, FViewport* InViewport, FSceneViewFamilyContext& ViewFamily) {

	FSceneView* outView = nullptr;

	for (FLocalPlayerIterator Iterator(GEngine, MyWorld); Iterator; ++Iterator)
	{
		ULocalPlayer* LocalPlayer = *Iterator;
		if (LocalPlayer)
		{
			APlayerController* PlayerController = LocalPlayer->PlayerController;

			const bool bEnableStereo = GEngine->IsStereoscopic3D(InViewport);
			const int32 NumViews = 1;

			for (int32 ViewIndex = 0; ViewIndex < NumViews; ++ViewIndex)
			{
				// Calculate the player's view information.
				FVector		ViewLocation;
				FRotator	ViewRotation;

				FSceneView* View = LocalPlayer->CalcSceneView(&ViewFamily, ViewLocation, ViewRotation, InViewport, nullptr,  INDEX_NONE);
				outView = View;
				if (View)
				{				

					if (View->Family->EngineShowFlags.Wireframe)
					{
						// Wireframe color is emissive-only, and mesh-modifying materials do not use material substitution, hence...
						View->DiffuseOverrideParameter = FVector4f(0.f, 0.f, 0.f, 0.f);
						View->SpecularOverrideParameter = FVector4f(0.f, 0.f, 0.f, 0.f);
					}
					else if (View->Family->EngineShowFlags.OverrideDiffuseAndSpecular)
					{
						View->DiffuseOverrideParameter = FVector4f(GEngine->LightingOnlyBrightness.R, GEngine->LightingOnlyBrightness.G, GEngine->LightingOnlyBrightness.B, 0.0f);
						View->SpecularOverrideParameter = FVector4f(.1f, .1f, .1f, 0.0f);
					}
					else if (View->Family->EngineShowFlags.LightingOnlyOverride)
					{
						View->DiffuseOverrideParameter = FVector4f(GEngine->LightingOnlyBrightness.R, GEngine->LightingOnlyBrightness.G, GEngine->LightingOnlyBrightness.B, 0.0f);
						View->SpecularOverrideParameter = FVector4f(0.f, 0.f, 0.f, 0.f);
					}
					else if (View->Family->EngineShowFlags.ReflectionOverride)
					{
						View->DiffuseOverrideParameter = FVector4f(0.f, 0.f, 0.f, 0.f);
						View->SpecularOverrideParameter = FVector4f(1, 1, 1, 0.0f);
						View->NormalOverrideParameter = FVector4f(0, 0, 1, 0.0f);
						View->RoughnessOverrideParameter = FVector2D(0.0f, 0.0f);
					}

					if (!View->Family->EngineShowFlags.Diffuse)
					{
						View->DiffuseOverrideParameter = FVector4f(0.f, 0.f, 0.f, 0.f);
					}

					if (!View->Family->EngineShowFlags.Specular)
					{
						View->SpecularOverrideParameter = FVector4f(0.f, 0.f, 0.f, 0.f);
					}

					View->CurrentBufferVisualizationMode = NAME_None;
					View->CurrentNaniteVisualizationMode = NAME_None;
					View->CurrentLumenVisualizationMode = NAME_None;
					View->CurrentStrataVisualizationMode = NAME_None;
					View->CurrentGroomVisualizationMode = NAME_None;
					View->CurrentVirtualShadowMapVisualizationMode = NAME_None;

					View->CameraConstrainedViewRect = View->UnscaledViewRect;

					// If this is the primary drawing pass, update things that depend on the view location
					if (ViewIndex == 0)
					{
						// Save the location of the view.
						LocalPlayer->LastViewLocation = ViewLocation;						
						
					}

					// Add view information for resource streaming. Allow up to 5X boost for small FOV.
					const float StreamingScale = 1.f / FMath::Clamp<float>(View->LODDistanceFactor, .2f, 1.f);
					IStreamingManager::Get().AddViewInformation(View->ViewMatrices.GetViewOrigin(), View->UnscaledViewRect.Width(), View->UnscaledViewRect.Width() * View->ViewMatrices.GetProjectionMatrix().M[0][0], StreamingScale);
					MyWorld->ViewLocationsRenderedLastFrame.Add(View->ViewMatrices.GetViewOrigin());
					FWorldCachedViewInfo& WorldViewInfo = MyWorld->CachedViewInfoRenderedLastFrame.AddDefaulted_GetRef();
					WorldViewInfo.ViewMatrix = View->ViewMatrices.GetViewMatrix();
					WorldViewInfo.ProjectionMatrix = View->ViewMatrices.GetProjectionMatrix();
					WorldViewInfo.ViewProjectionMatrix = View->ViewMatrices.GetViewProjectionMatrix();
					WorldViewInfo.ViewToWorld = View->ViewMatrices.GetInvViewMatrix();
					MyWorld->LastRenderTime = MyWorld->GetTimeSeconds();
				}
			}
		}

		return outView;
			 
	}
	return nullptr;
}

void FScreenshotViewportClient::Draw(FViewport* InViewport, FCanvas* SceneCanvas)
{

	//不经行立体绘制
	const bool bStereoRendering = false;

	//Valid SceneCanvas is required.  Make this explicit.
	check(SceneCanvas);

	// Create a temporary canvas if there isn't already one.
	//如果还没有临时画布，请创建一个临时画布。
	static FName CanvasObjectName(TEXT("STCanvasObject"));
	UCanvas* CanvasObject = GetCanvasByName(CanvasObjectName);
	CanvasObject->Canvas = SceneCanvas;

	SceneCanvas->SetScaledToRenderTarget(bStereoRendering);
	SceneCanvas->SetStereoRendering(bStereoRendering);


	UWorld* MyWorld = GetWorld();
	if (MyWorld == nullptr)
	{
		return;
	}

	// create the view family for rendering the world scene to the viewport's render target
	FSceneViewFamilyContext ViewFamily(FSceneViewFamily::ConstructionValues(
		InViewport,
		MyWorld->Scene,
		ScreenshotShowFlags)
		.SetRealtimeUpdate(true));
	ViewFamily.ViewMode = UimageInfoLib::CastShowFlags(screenshotInfo.flags);



	FSceneViewExtensionContext ViewExtensionContext(InViewport);
	//启用立体声？
	ViewExtensionContext.bStereoEnabled = true;
	//可以修改渲染线程上的视图参数的扩展？
	ViewFamily.ViewExtensions = GEngine->ViewExtensions->GatherActiveExtensions(ViewExtensionContext);

	for (auto ViewExt : ViewFamily.ViewExtensions)
	{
		ViewExt->SetupViewFamily(ViewFamily);
	}



	EngineShowFlagOverride(ESFIM_Game, ViewFamily.ViewMode, ViewFamily.EngineShowFlags, false);


	// Setup the screen percentage and upscaling method for the view family.
	bool bFinalScreenPercentageShowFlag;
	bool bUsesDynamicResolution = false;
	{
		checkf(ViewFamily.GetScreenPercentageInterface() == nullptr,
			TEXT("Some code has tried to set up an alien screen percentage driver, that could be wrong if not supported very well by the RHI."));

		// Force screen percentage show flag to be turned off if not supported.
		if (!ViewFamily.SupportsScreenPercentage())
		{
			ViewFamily.EngineShowFlags.ScreenPercentage = false;
		}

		// Set up secondary resolution fraction for the view family.
		if (ViewFamily.SupportsScreenPercentage())
		{
			//直接设置为暂定值
			ViewFamily.SecondaryViewFraction = FMath::Min(CustomSecondaruScreenPercentage / 100.0f, 1.0f);
		}



		bFinalScreenPercentageShowFlag = ViewFamily.EngineShowFlags.ScreenPercentage;
	}
	//要渲染的view
	FSceneView* drawSceneView = nullptr;
	{



		// Calculate the player's view information.
		FVector		ViewLocation;
		FRotator	ViewRotation;
		drawSceneView = calcSceneLocalPlayer->CalcSceneView(&ViewFamily, ViewLocation, ViewRotation, InViewport, nullptr);
		
		if (drawSceneView)
		{
			if (drawSceneView->Family->EngineShowFlags.Wireframe)
			{
				// Wireframe color is emissive-only, and mesh-modifying materials do not use material substitution, hence...
				drawSceneView->DiffuseOverrideParameter = FVector4f(0.f, 0.f, 0.f, 0.f);
				drawSceneView->SpecularOverrideParameter = FVector4f(0.f, 0.f, 0.f, 0.f);
			}
			else if (drawSceneView->Family->EngineShowFlags.OverrideDiffuseAndSpecular)
			{
				drawSceneView->DiffuseOverrideParameter = FVector4f(GEngine->LightingOnlyBrightness.R, GEngine->LightingOnlyBrightness.G, GEngine->LightingOnlyBrightness.B, 0.0f);
				drawSceneView->SpecularOverrideParameter = FVector4f(.1f, .1f, .1f, 0.0f);
			}
			else if (drawSceneView->Family->EngineShowFlags.LightingOnlyOverride)
			{
				drawSceneView->DiffuseOverrideParameter = FVector4f(GEngine->LightingOnlyBrightness.R, GEngine->LightingOnlyBrightness.G, GEngine->LightingOnlyBrightness.B, 0.0f);
				drawSceneView->SpecularOverrideParameter = FVector4f(0.f, 0.f, 0.f, 0.f);
			}
			else if (drawSceneView->Family->EngineShowFlags.ReflectionOverride)
			{
				drawSceneView->DiffuseOverrideParameter = FVector4f(0.f, 0.f, 0.f, 0.f);
				drawSceneView->SpecularOverrideParameter = FVector4f(1, 1, 1, 0.0f);
				drawSceneView->NormalOverrideParameter = FVector4f(0, 0, 1, 0.0f);
				drawSceneView->RoughnessOverrideParameter = FVector2D(0.0f, 0.0f);
			}

			if (!drawSceneView->Family->EngineShowFlags.Diffuse)
			{
				drawSceneView->DiffuseOverrideParameter = FVector4f(0.f, 0.f, 0.f, 0.f);
			}

			if (!drawSceneView->Family->EngineShowFlags.Specular)
			{
				drawSceneView->SpecularOverrideParameter = FVector4f(0.f, 0.f, 0.f, 0.f);
			}

			drawSceneView->CurrentBufferVisualizationMode = CurrentBufferVisualizationMode;
			drawSceneView->CurrentNaniteVisualizationMode = CurrentNaniteVisualizationMode;
			drawSceneView->CurrentLumenVisualizationMode = CurrentLumenVisualizationMode;
			drawSceneView->CurrentStrataVisualizationMode = CurrentStrataVisualizationMode;
			drawSceneView->CurrentGroomVisualizationMode = CurrentGroomVisualizationMode;
			drawSceneView->CurrentVirtualShadowMapVisualizationMode = CurrentVirtualShadowMapVisualizationMode;

			drawSceneView->CameraConstrainedViewRect = drawSceneView->UnscaledViewRect;
		}
		else {
			return;
		}
	}


	// Update level streaming.
	MyWorld->UpdateLevelStreaming();

	// Find largest rectangle bounded by all rendered views.
	uint32 MinX = InViewport->GetSizeXY().X, MinY = InViewport->GetSizeXY().Y, MaxX = 0, MaxY = 0;
	uint32 TotalArea = 0;
	{
		for (int32 ViewIndex = 0; ViewIndex < ViewFamily.Views.Num(); ++ViewIndex)
		{
			const FSceneView* View = ViewFamily.Views[ViewIndex];

			FIntRect UpscaledViewRect = View->UnscaledViewRect;

			MinX = FMath::Min<uint32>(UpscaledViewRect.Min.X, MinX);
			MinY = FMath::Min<uint32>(UpscaledViewRect.Min.Y, MinY);
			MaxX = FMath::Max<uint32>(UpscaledViewRect.Max.X, MaxX);
			MaxY = FMath::Max<uint32>(UpscaledViewRect.Max.Y, MaxY);
			TotalArea += UpscaledViewRect.Width() * UpscaledViewRect.Height();
		}

		// To draw black borders around the rendered image (prevents artifacts from post processing passes that read outside of the image e.g. PostProcessAA)
		{
			int32 BlackBorders = FMath::Clamp(m_BlackBorders, 0, 10);

			if (ViewFamily.Views.Num() == 1 && BlackBorders)
			{
				MinX += BlackBorders;
				MinY += BlackBorders;
				MaxX -= BlackBorders;
				MaxY -= BlackBorders;
				TotalArea = (MaxX - MinX) * (MaxY - MinY);
			}
		}
	}

	// If the views don't cover the entire bounding rectangle, clear the entire buffer.
	bool bBufferCleared = false;
	bool bStereoscopicPass = (ViewFamily.Views.Num() != 0 && IStereoRendering::IsStereoEyeView(*ViewFamily.Views[0]));
	if (ViewFamily.Views.Num() == 0 || TotalArea != (MaxX - MinX) * (MaxY - MinY) || bStereoscopicPass)
	{
		if (!bStereoscopicPass) // TotalArea computation does not work correctly for stereoscopic views
		{
			SceneCanvas->Clear(FLinearColor::Transparent);
		}

		bBufferCleared = true;
	}

	{
		// If a screen percentage interface was not set by dynamic resolution, then create one matching legacy behavior.
		if (ViewFamily.GetScreenPercentageInterface() == nullptr)
		{
			float GlobalResolutionFraction = 1.0f;

			if (ViewFamily.EngineShowFlags.ScreenPercentage && ViewFamily.Views.Num() > 0)
			{
				// Get global view fraction.
				FStaticResolutionFractionHeuristic StaticHeuristic;
				StaticHeuristic.Settings.PullRunTimeRenderingSettings(GetViewStatusForScreenPercentage());
				StaticHeuristic.PullViewFamilyRenderingSettings(ViewFamily);
				StaticHeuristic.DPIScale = GetDPIScale();

				GlobalResolutionFraction = StaticHeuristic.ResolveResolutionFraction();
			}

			ViewFamily.SetScreenPercentageInterface(new FLegacyScreenPercentageDriver(
				ViewFamily, GlobalResolutionFraction));
		}

		check(ViewFamily.GetScreenPercentageInterface() != nullptr);

		// Make sure the engine show flag for screen percentage is still what it was when setting up the screen percentage interface
		ViewFamily.EngineShowFlags.ScreenPercentage = bFinalScreenPercentageShowFlag;


	}

	ViewFamily.bIsHDR = m_useHDR;

	//绘制
	// Draw the player views.
	GetRendererModule().BeginRenderingViewFamily(SceneCanvas, &ViewFamily);

	// Beyond this point, only UI rendering independent from dynamc resolution.
	GEngine->EmitDynamicResolutionEvent(EDynamicResolutionStateEvent::EndDynamicResolutionRendering);

	// Clear areas of the rendertarget (backbuffer) that aren't drawn over by the views.
	if (!bBufferCleared)
	{
		// clear left
		if (MinX > 0)
		{
			SceneCanvas->DrawTile(0, 0, MinX, InViewport->GetSizeXY().Y, 0.0f, 0.0f, 1.0f, 1.f, FLinearColor::Black, NULL, false);
		}
		// clear right
		if (MaxX < (uint32)InViewport->GetSizeXY().X)
		{
			SceneCanvas->DrawTile(MaxX, 0, InViewport->GetSizeXY().X, InViewport->GetSizeXY().Y, 0.0f, 0.0f, 1.0f, 1.f, FLinearColor::Black, NULL, false);
		}
		// clear top
		if (MinY > 0)
		{
			SceneCanvas->DrawTile(MinX, 0, MaxX, MinY, 0.0f, 0.0f, 1.0f, 1.f, FLinearColor::Black, NULL, false);
		}
		// clear bottom
		if (MaxY < (uint32)InViewport->GetSizeXY().Y)
		{
			SceneCanvas->DrawTile(MinX, MaxY, MaxX, InViewport->GetSizeXY().Y, 0.0f, 0.0f, 1.0f, 1.f, FLinearColor::Black, NULL, false);
		}
	}

	// Remove temporary debug lines.
	if (MyWorld->LineBatcher != nullptr)
	{
		MyWorld->LineBatcher->Flush();
	}

	if (MyWorld->ForegroundLineBatcher != nullptr)
	{
		MyWorld->ForegroundLineBatcher->Flush();
	}
}

TOptional<bool> FScreenshotViewportClient::QueryShowFocus(const EFocusCause InFocusCause) const
{
	UUserInterfaceSettings* UISettings = GetMutableDefault<UUserInterfaceSettings>(UUserInterfaceSettings::StaticClass());

	if (UISettings->RenderFocusRule == ERenderFocusRule::Never ||
		(UISettings->RenderFocusRule == ERenderFocusRule::NonPointer && InFocusCause == EFocusCause::Mouse) ||
		(UISettings->RenderFocusRule == ERenderFocusRule::NavigationOnly && InFocusCause != EFocusCause::Navigation))
	{
		return false;
	}

	return true;
}

bool  FScreenshotViewportClient::IsOrtho() const
{
	return false;
}

EViewStatusForScreenPercentage FScreenshotViewportClient::GetViewStatusForScreenPercentage() const
{
	if (ScreenshotShowFlags.PathTracing)
	{
		return EViewStatusForScreenPercentage::PathTracer;
	}
	else if (ScreenshotShowFlags.StereoRendering)
	{
		return EViewStatusForScreenPercentage::VR;
	}
	else if (worldData.world && worldData.world->GetFeatureLevel() == ERHIFeatureLevel::ES3_1)
	{
		return EViewStatusForScreenPercentage::Mobile;
	}
	else
	{
		return EViewStatusForScreenPercentage::Desktop;
	}
}



